#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include "Pthread.hpp"
#include "Cond.hpp"
#include "Log.hpp"

using namespace PthreadModule;
using namespace CondModule;
using namespace LogModule;

namespace PthreadPoolModule
{

    const int defaultNum = 5;
    template <class T>
    class PthreadPool 
    {
    private:
        void defaultHandle(std::string name)
        {
            while (true)
            {
                LockGuard lock(_lock);
                // 判断是否可以拿任务
                while (_taskq.size() == 0 && _isrunning)
                {
                    _wait_num++;
                    LOG(LogModule::LogLevel::INFOMATION) << name << "进程开始休眠....";
                    _cond.Wait(_lock.Ptr());
                    LOG(LogModule::LogLevel::INFOMATION) << name << "进程唤醒....";
                    _wait_num--;
                }

                if (!_isrunning && _taskq.size() == 0)
                {
                    break; // 线程开始结束 解决完全部任务
                }

                // 拿取任务
                T t = _taskq.front();
                _taskq.pop();

                t(); // 执行任务
            }

            LOG(LogModule::LogLevel::INFOMATION) << name << "线程退出....";
        }
        PthreadPool(int num = defaultNum) : _num(num),
                                            _wait_num(0),
                                            _isrunning(false)
        {
            _threads.reserve(num);
            for (int i = 0; i < num; i++)
            {

                _threads.emplace_back([this, i]()
                                      { this->defaultHandle("thread-" + std::to_string(i + 1)); });
                LOG(LogLevel::DEBUG) << "插入线程成功";
            }
        }

    public:
        static PthreadPool<T> *getInstance()
        {
            if (instance == NULL)
            {
                LockGuard lockguard(PthreadPool<T>::mutex);
                if (instance == NULL)
                {
                    LOG(LogLevel::INFOMATION) << "单例首次被执行，需要创建对象...";

                    LOG(LogLevel::DEBUG) << "开始创建";
                    instance = new PthreadPool<T>(5);

                    LOG(LogLevel::DEBUG) << "所有线程创建成功";

                    instance->Start();
                    LOG(LogLevel::DEBUG) << "启动成功";
                }
            }

            return instance;
        }

        void Start()
        {
            if (!_isrunning)
            {
                _isrunning = true; // 位置放在前面，避免线程启动后其余退出
                for (auto &e : _threads)
                {
                    e.Start();
                    LOG(LogModule::LogLevel::INFOMATION) << "启动线程" << e.Name() << " ... 成功";
                }
            }
        }

        void Stop()
        {
            LockGuard lock(_lock);
            if (_isrunning)
            {
                _isrunning = false;
                _cond.Broadcast(); // 解决剩余任务
                LOG(LogModule::LogLevel::INFOMATION) << "线程池退出广播....";
            }
        }

        void Join()
        {
            for (auto &e : _threads)
            {
                e.Join();
                LOG(LogModule::LogLevel::INFOMATION) << "回收线程" << e.Name() << " ... 成功";
            }
        }

        void Enque(T &&in)
        {
            LockGuard lock(_lock);
            if (_isrunning)
            {
                _taskq.emplace(in);

                if (_wait_num)
                    _cond.Broadcast();
            }
        }

    private:
        std::vector<Pthread> _threads;
        int _num;
        int _wait_num;

        std::queue<T> _taskq; // 临界资源
        Mutex _lock;
        Cond _cond;

        bool _isrunning;

        static PthreadPool<T> *instance;
        static Mutex mutex; // 只用来保护单例
    };

    template <class T>
    PthreadPool<T> *PthreadPool<T>::instance = NULL;
    template <class T>
    Mutex PthreadPool<T>::mutex; // 只用来保护单例
}